use std::mem;

use wasm_context::{
    FunctionKind,
    FnTy,
    Import,
    Export,
    Instruction,
    Context
};

pub fn process( ctx: &mut Context ) {
    let already_done = ctx.functions.iter().any( |(_, function)| {
        match function {
            &FunctionKind::Import { import: Import { ref module, ref field }, .. } if module == "env" && field == "__web_on_grow" => {
                true
            },
            _ => false
        }
    });

    if already_done {
        return;
    }

    let type_index = ctx.get_or_add_fn_type( FnTy { params: vec![], return_type: None } );
    let on_grow_function_index = ctx.add_function( FunctionKind::Import {
        type_index,
        export: Export::none(),
        import: Import {
            module: "env".to_owned(),
            field: "__web_on_grow".to_owned()
        },
        name: Some( "__web_on_grow".to_owned() )
    });

    ctx.patch_code( |instructions| {
        let should_process = instructions.iter().any( |instruction| {
            match instruction {
                &Instruction::GrowMemory( _ ) => true,
                _ => false
            }
        });

        if !should_process {
            return;
        }

        let mut new_instructions = Vec::with_capacity( instructions.len() );
        let mut old_instructions = Vec::new();
        mem::swap( instructions, &mut old_instructions );

        for instruction in old_instructions {
            let should_insert = match &instruction {
                &Instruction::GrowMemory( _ ) => true,
                &_ => false
            };

            new_instructions.push( instruction );
            if should_insert {
                new_instructions.push( Instruction::Call( on_grow_function_index ) );
            }
        }

        *instructions = new_instructions;
    });
}
